﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Collections;

namespace Sinacor.Infra.UI.Common
{
    /// <summary>
    /// Contém informações sobre a alteração de um item
    /// </summary>
    [Serializable]
    internal class ItemState<T>
        where T : BaseModel 
    {
        /// <summary>
        /// Construtor que inicializa índice, status e versão original do item
        /// </summary>
        /// <param name="index">Índice do item na coleção original</param>
        /// <param name="status">Indica o tipo de alteração que o item sofreu</param>
        /// <param name="originalItem">Versão original do item</param>
        public ItemState(int index, State status, T originalItem)
        {
            _index = index;
            _status = status;
            _originalItem = originalItem;
        }

        private int _index;
        /// <summary>
        /// Índice do item na coleção original
        /// </summary>
        public int Index
        {
            get { return _index; }
            set { _index = value; }
        }

        private State _status;
        /// <summary>
        /// Indica o tipo de alteração que o item sofreu
        /// </summary>
        public State Status
        {
            get { return _status; }
        }

        private T _originalItem;
        /// <summary>
        /// Versão original do item
        /// </summary>
        public T OriginalItem
        {
            get { return _originalItem; }
        }
    }

    #region ItemStateCollection
    /// <summary>
    /// Coleção com as alterações nos itens
    /// </summary>
    [Serializable]
    internal class ItemStateCollection<T> : ObservableCollection<ItemState<T>>
        where T : BaseModel 
    {
        /// <summary>
        /// Adiciona um item inserido
        /// </summary>
        /// <param name="index">Índice do item na coleção original</param>
        public void AddInserted(int index)
        {
            this.Add(new ItemState<T>(index, State.Inserted, null));
        }

        /// <summary>
        /// Adiciona um item alterado
        /// </summary>
        /// <param name="index">Índice do item na coleção original</param>
        /// <param name="originalItem">Versão original do item</param>
        public void AddUpdated(int index, T originalItem)
        {
            this.Add(new ItemState<T>(index, State.Updated, originalItem));
        }

        /// <summary>
        /// Adiciona um item excluído
        /// </summary>
        /// <param name="index">Índice do item na coleção original</param>
        public void AddDeleted(int index)
        {
            ItemState<T> itemState = this.FindLastByIndex(index);
            if (itemState == null || itemState.Status != State.Deleted)
            {
                this.Add(new ItemState<T>(index, State.Deleted, null));
            }
        }

        /// <summary>
        /// Busca o primeiro item pelo índice da coleção original
        /// </summary>
        /// <param name="index">Índice do item na coleção original</param>
        /// <returns>ItemState se encontrar, null se não encontrar</returns>
        public ItemState<T> FindByIndex(int index)
        {
            return this.ToList().Find(delegate(ItemState<T> target)
            {
                return (target.Index == index);
            }
                            );
        }

        /// <summary>
        /// Busca todos os itens pelo índice da coleção original
        /// </summary>
        /// <param name="index">Índice do item na coleção original</param>
        /// <returns>Coleção com ItemState encontrados</returns>
        public ReadOnlyCollection<ItemState<T>> FindAllByIndex(int index)
        {
            return this.ToList().FindAll(delegate(ItemState<T> target)
            {
                return (target.Index == index);
            }
                               ).ToList().AsReadOnly();
        }

        /// <summary>
        /// Busca o último item pelo índice da coleção original
        /// </summary>
        /// <param name="index">Índice do item na coleção original</param>
        /// <returns>ItemState se encontrar, null se não encontrar</returns>
        public ItemState<T> FindLastByIndex(int index)
        {
            return this.ToList().FindLast(delegate(ItemState<T> target)
            {
                return (target.Index == index);
            }
                                );
        }

        /// <summary>
        /// Busca todos os itens com um determinado status
        /// </summary>
        /// <param name="status">Indica o tipo de alteração que o item sofreu</param>
        /// <returns>Coleção com ItemState encontrados</returns>
        public ReadOnlyCollection<ItemState<T>> FindAllByStatus(State status)
        {
            return this.ToList().FindAll(delegate(ItemState<T> target)
            {
                return (target.Status == status);
            }
                               ).ToList().AsReadOnly();
        }

        /// <summary>
        /// Remove os itens pelo index
        /// </summary>
        /// <param name="index"></param>
        public void RemoveByIndex(int index)
        {
            for (int i = this.Count - 1; i >= 0; i--)
            {
                if (this[i].Index == index)
                {
                    this.RemoveAt(i);
                }
            }
        }

        /// <summary>
        /// Buscas as primeiras ações de todos os itens
        /// </summary>
        /// <returns></returns>
        public List<ItemState<T>> GetFirstActions()
        {
            List<ItemState<T>> primeirasAcoesCollection = new List<ItemState<T>>();

            foreach (ItemState<T> item in this)
            {
                ItemState<T> itemProcurado = primeirasAcoesCollection.Find(delegate(ItemState<T> target) { return (target.Index == item.Index); });
                if (itemProcurado == null)
                    primeirasAcoesCollection.Add(item);

            }

            return primeirasAcoesCollection;
        }

        /// <summary>
        /// Busca as últimas ações de cada Item
        /// </summary>
        /// <returns></returns>
        public List<ItemState<T>> GetLastActions()
        {
            List<ItemState<T>> ultimasAcoesCollection = new List<ItemState<T>>();

            for (int i = (this.Count - 1); i >= 0; i--)
            {
                ItemState<T> item = this[i];
                ItemState<T> itemProcurado = ultimasAcoesCollection.Find(delegate(ItemState<T> target) { return (target.Index == item.Index); });
                if (itemProcurado == null)
                    ultimasAcoesCollection.Add(item);

            }

            return ultimasAcoesCollection;
        }

        /// <summary>
        /// Busca as últimas ações de cada item verificando o status do item
        /// </summary>
        /// <returns></returns>
        public List<ItemState<T>> GetLastActions(State status)
        {
            List<ItemState<T>> ultimasAcoesCollection = GetLastActions();
            return ultimasAcoesCollection.FindAll(delegate(ItemState<T> target) { return (target.Status == status); });
        }

        /// <summary>
        /// Procura a primeira ocorrencia de update para retornar o OriginalItem
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public T SearchFirstUpdatedItem(int index)
        {
            T firstOriginalItem = null;
            ReadOnlyCollection<ItemState<T>> itemCollection = this.FindAllByIndex(index);

            foreach (ItemState<T> item in itemCollection)
            {
                if (item.Status == State.Updated)
                {
                    firstOriginalItem = item.OriginalItem;
                    break;
                }
            }

            return firstOriginalItem;
        }
    }
    #endregion
}
